<?php
/*!
 * LostKids PHP Library v0.1 
 * http://huali.li/
 *
 * Copyright 2011, 
 * Written By James Pang
 *  
 * Date: 2011/2/14
 */

require_once ('../configs.php');

abstract class MsgSrcPluginAbs implements MsgSrcPlugin
{
    /** msg contains more than one keyword */
    const MSG_ERR_NONCMD = 'ERR_NOCMD';
    const MSG_INVLAD_NONCMD = 'ERR_INVALID_CMD';
    /** msg contains more than one keyword */
    const MSG_ERR_CONFLICT = 'ERR_CONFLICT';
    /** need this when match/confirm */
    const MSG_ERR_NEED_MATCHID = 'ERR_NEED_MATCHID';
    const MSG_ERR_INVALID_MATCHID = 'ERR_INVALID_MATCHID';
    const MSG_ERR_NEEDIMG = 'ERR_NEED_IMG';

    protected $dm;

    protected function __construct() {
       $this->dm = Assembly::getService('DataManager', true);
    }

    public function retrieveMsgs($dataSrcKey, $maxCnt = 100) {
        $ret = array();

        $sinceId = $this->dm->getLastMsgCursor($dataSrcKey);
        $msgsList = $this->getMsgsList($dataSrcKey, $maxCnt, $sinceId);
        $msgs = $msgsList[0];
        if(count($msgs) <= 0)
            return $ret;

        foreach ( $msgs as $msg ) {
            $message = $this->parseMsg($msg, $dataSrcKey);
            if($message == null)
                continue;
            $this->dm->saveMsg($message);
            $ret[] = $message;
        }
        if(count($msgs) <= 0)
            return;

        // batch all msgs
        $ret = $this->messageBatch($ret);

        // record cursors
        if($msgsList[1])
            $this->dm->saveLastMsgCursor($dataSrcKey, $msgsList[1]);

        return $ret;
    }

    public function respondMsg($msgId, $ownerId, $content, $withUrl = false) {
    	if(!$this->respondByMsgId($msgId, $content, $withUrl)) {
    		$this->respondByOwnerId($ownerId, $content, $withUrl);
    	}
    }

    /**
     * get message batch from DB
     */
    protected function messageBatch($msgs) {
        $ret = array();
        $batchKeys = array();
        foreach ( $msgs as $m ) {
            $batchKey = BatchMessage::genNormBatchKey($m);
            if(!$m->getBatchKey()) {
                $ret[] = $m;
            } else if(!in_array($batchKey, $batchKeys)) {
                $bmsgs = $this->dm->getBatchMsgs($m);
                if(empty($bmsgs))
                    throw new BizException('Cannot find batch, impposible, batchKey='.$batchKey);

                $bmsg = null;
                foreach($bmsgs as $bm) {
                	if($bmsg != null)
                        $bmsg->addBatch($bm);
                    else {
                        $bmsg = new BatchMessage($bm);
                    }
                }
                $batchKeys[] = $batchKey;
                $ret[] = $bmsg;
            }
        }
        return $ret;
    }

    /**
     * @return array[array, string] array[0] is msgs list, array[1] is new sinceid
     */
    protected abstract function getMsgsList($dataSrcKey, $maxCnt, $sinceId);

    protected abstract function getMsgContent($msg);

    protected abstract function getImgUrl($msg);

    protected abstract function respondByMsgId($msgId, $content);

    protected abstract function respondByOwnerId($ownerId, $content);

    protected function parseMsg($msg, $dataSrcKey) {
        // must utf-8 encoding
        $imgUrl = trim($this->getImgUrl($msg));
        $cmd = $this->getMsgCommand($this->getMsgContent($msg), $imgUrl);
        $type = @$cmd['type'];
        $ownerId = $msg['user']['id'];

        // invalid msg
        if(isset($cmd['error'])) {
            // no command, no img, needn't respond
            if($cmd['error'] == self::MSG_ERR_NONCMD && !$imgUrl)
                return null;

            $this->respondMsg($msg['id'], $ownerId, ResourceBundle::getMsg($cmd['error']), false);
            return null;
        }

        $ret = new WeiboMessage($dataSrcKey, $ownerId, $msg['id'], @$cmd['batchKey'],
                    $imgUrl, $type, $cmd['isSeek'], $msg['text'], @$cmd['matchId']);
        return $ret;
    }

    /**
     * @param string $text, must be utf-8 encoding
     * @return array always non-null, error in error['error']
     */
    protected function getMsgCommand($text, $imgUrl) {
        $text = $this->preProcess($text);

        $cmd = array();
        if(!$text) {
            $cmd['error'] = self::MSG_ERR_NONCMD;
            return $cmd;
        } else if(!(strpos($text, MsgSrcPlugin::KEY_DELIM1) === false
                && strpos($text, MsgSrcPlugin::KEY_DELIM1_ALT) === false)){
            $cmd['error'] = self::MSG_ERR_CONFLICT;
            return $cmd;
        }

        $token = 0; $idx = -1;
        if(!(($temp = strpos($text, MsgSrcPlugin::KEY_REPORT)) === false)){
            $idx = $temp;
            $token += 1;
        }
        if(!(($temp = strpos($text, MsgSrcPlugin::KEY_SEEK)) === false)){
            $idx = $temp;
            $token += 2;
        }
        if(!(($temp = strpos($text, MsgSrcPlugin::KEY_NON_MATCH)) === false)){
            $idx = $temp;
            $token += 4;
        }
        if(!(($temp = strpos($text, MsgSrcPlugin::KEY_MATCH)) === false)){
            $idx = $temp;
            $token += 8;
        }
        if(!(($temp = strpos($text, MsgSrcPlugin::KEY_CONFIRM)) === false)){
            $idx = $temp;
            $token += 16;
        }

        if($idx < 0) {
            $cmd['error'] = self::MSG_ERR_NONCMD;
            return $cmd;
        } else if($idx > 0) {
            $cmd['error'] = $token & ($token - 1) == 0 ? // is 2^n
                        self::MSG_INVLAD_NONCMD : self::MSG_ERR_CONFLICT;
            return $cmd;
        }

        // now $idx == 0
        switch($token) {
        case 1:
            $cmd['type'] = WeiboMessage::TYPE_REPORT;
            $cmd['batchKey'] = substr($text, strlen(MsgSrcPlugin::KEY_REPORT));
            if(!$imgUrl) {
            	$cmd['error'] = self::MSG_ERR_NEEDIMG;
            }
            $cmd['isSeek'] = false;
            return $cmd;
        case 2:
            $cmd['type'] = WeiboMessage::TYPE_SEEK;
            $cmd['batchKey'] = substr($text, strlen(MsgSrcPlugin::KEY_SEEK));
            if(!$imgUrl) {
            	$cmd['error'] = self::MSG_ERR_NEEDIMG;
            }
            $cmd['isSeek'] = true;
            return $cmd;
        case 4:
            $cmd['type'] = WeiboMessage::TYPE_NON_MATCH;
            $cmd['isSeek'] = true;// FIXME TODO
            return $cmd;
        case 8:
            $cmd['type'] = WeiboMessage::TYPE_MATCH;
            $matchId = substr($text, strlen(MsgSrcPlugin::KEY_MATCH));
            if($matchId) {
                if(Utils::isNumeric($matchId))
                    $cmd['matchId'] = $matchId;
                else
                    $cmd['error'] = self::MSG_ERR_INVALID_MATCHID;
            } else {
                $cmd['error'] = self::MSG_ERR_NEED_MATCHID;
            }
            $cmd['isSeek'] = true;// FIXME TODO
            return $cmd;
        case 16:
            $cmd['type'] = WeiboMessage::TYPE_CONFIRM;
            $matchId = substr($text, strlen(MsgSrcPlugin::KEY_CONFIRM));
            if($matchId) {
                if(Utils::isNumeric($matchId))
                    $cmd['matchId'] = $matchId;
                else
                    $cmd['error'] = self::MSG_ERR_INVALID_MATCHID;
            } else {
                $cmd['error'] = self::MSG_ERR_NEED_MATCHID;
            }
            return $cmd;
        default:
            $cmd['error'] = $token == 0 ? self::MSG_ERR_NONCMD : self::MSG_ERR_CONFLICT;
            return $cmd;
        }
    }

    /**
     * split by delims
     */
    private function preProcess($text) {
        $idx1 = strpos($text, MsgSrcPlugin::KEY_DELIM1);
        if(!($idx1 === false)) {
            $idx2 = strrpos($text, MsgSrcPlugin::KEY_DELIM1);
            if(!($idx2 === false) && $idx2 > $idx1)
                return trim(substr($text, $idx1 + 1, $idx2 - $idx1 - 1));
        } else {
            $idx1 = strpos($text, MsgSrcPlugin::KEY_DELIM1_ALT);
            if(!($idx1 === false)) {
                $idx2 = strrpos($text, MsgSrcPlugin::KEY_DELIM1_ALT, $idx1);
                if(!($idx2 === false) && $idx2 > $idx1)
                    return trim(substr($text, $idx1 + 1, $idx2 - $idx1 - 1));
            }
        }
        return null;
    }
}
?>
